<template>
  <div class="fx-tabs" :class="[`fx-tabs-${type}`]">
    <div class="fx-tabs-title-wrapper">
      <span v-if="pathBtnShow" @click.stop="pathClick('left')"><i class="iconfont icon-left"/></span>
      <div class="fx-tabs-title-inner" ref="tabsTitleInnerDom">
        <ul class="fx-tabs-title">
          <transition-group name="close">
            <li v-for="(item,index) in tabsTitleList" :key="item.name"
                :class="{active:item.name===currentActive}"
                @click.stop="tabsTitleClick(item,index,$event)">
              <p v-tooltip="item.title" :style="titleStyle" fx-tooltip-text-over>
                {{ item.title }}
              </p>
              <span v-if="closable||item.closable" @click.stop="tabsCloseClick(item)">
                <i class="iconfont icon-multiply fs10"/>
              </span>
            </li>
          </transition-group>
          <li v-if="addable" @click.stop="addClick"><i class="iconfont icon-plus"/></li>
        </ul>
        <div class="fx-tabs-line" ref="tabsActiveLineDom" v-if="type==='line'"></div>
      </div>
      <span v-if="pathBtnShow" @click.stop="pathClick('right')"><i class="iconfont icon-right"/></span>
    </div>
    <div class="fx-tabs-content">
      <slot/>
    </div>
  </div>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  nextTick,
  onBeforeMount, onBeforeUnmount,
  onMounted,
  PropType,
  provide,
  ref,
  SetupContext,
  watch
} from 'vue'
import mitt, { Emitter } from 'mitt'
import { TabsItemProps } from '@/base/tabs/tabsItem.vue'
import { useModelWatch } from '@/base/hooks'
import { prefixStyle } from '@/base/utils'
import { codeRule } from '@/base/types'

type TabsListProps = Omit<TabsItemProps, ''>
const Transform = prefixStyle('transform') as number

type TabsType = 'card' | 'border-card' | 'line'

interface TabsProps {
  modelValue: string | number;
  type: TabsType;
  closable: boolean;
  addable: boolean;
  maxWidth: string;
}

export default defineComponent({
  name: 'fxTabs',
  props: {
    // 双向绑定
    modelValue: {
      type: [String, Number] as PropType<string | number>,
      required: true
    },
    // 标签页类型
    type: {
      type: String as PropType<TabsType>,
      default: 'line'
    },
    // 可关闭
    closable: {
      type: Boolean as PropType<boolean>,
      default: false
    },
    // 可添加
    addable: {
      type: Boolean as PropType<boolean>,
      default: false
    },
    // 标签头最大宽度
    maxWidth: {
      type: String as PropType<string>,
      default: null
    }
  },
  emits: ['tabClose', 'tabAdd', 'tabClick', 'update:modelValue', 'change'],
  setup (props: TabsProps, ctx: SetupContext) {
    // 数据监听器
    const tabsMitt: Emitter = mitt()
    // 标签页头列表
    const tabsTitleList = ref<TabsListProps[]>([])
    // 当前标签选中
    const currentActive = ref(props.modelValue)
    // line类型下 选中下划线DOM
    const tabsActiveLineDom = ref<HTMLElement>()
    // 超出长度
    const tabsTitleInnerDom = ref<HTMLElement>()
    // 是否显示方向按钮
    const pathBtnShow = ref<boolean>(false)
    // 标签内容动效切换方向
    const tabsActivePath = ref<'to-right' | 'to-left'>('to-right')

    // 限制标签页头部样式
    const titleStyle = computed(() => {
      const isCode = codeRule.test(props.maxWidth)
      return {
        width: isCode ? props.maxWidth : `${props.maxWidth}px`,
        textAlign: props.maxWidth ? 'center' : null
      }
    })

    useModelWatch(() => props.modelValue, currentActive)
    watch(pathBtnShow, (val: boolean) => {
      if (!val && tabsTitleInnerDom.value) {
        const children = tabsTitleInnerDom.value.children[0] as HTMLElement
        children.style[Transform] = 'translateX(0)'
      }
    })
    watch(() => props.type, (val: TabsType) => {
      if (val === 'line') nextTick(initLineType)
    })

    // 数据监听器发送
    provide('tabsMittEmit', tabsMitt.emit)
    // 当前选中
    provide('currentActive', computed(() => currentActive.value))
    // 切换方向
    provide('tabsActivePath', computed(() => tabsActivePath.value))

    // 标签内容数据处理
    const handleTabsItemEmit = (val: TabsListProps | undefined): void => {
      if (!val) return
      const has = tabsTitleList.value.some((item: TabsListProps) => item.name === val.name)
      if (!has) tabsTitleList.value.push(val)
    }

    // 切换标签头点击事件
    const tabsTitleClick = (val: TabsListProps, index: number, ev: MouseEvent) => {
      const target = ev.target as HTMLElement
      if (tabsActiveLineDom?.value && target) {
        tabsActiveLineDom.value.style.width = `${target.parentElement?.offsetWidth}px`
        tabsActiveLineDom.value.style.left = `${target.parentElement?.offsetLeft}px`
      }
      const activeIndex = tabsTitleList.value.findIndex((item: TabsListProps) => currentActive.value === item.name)
      tabsActivePath.value = index < activeIndex ? 'to-right' : 'to-left'
      currentActive.value = val.name
      ctx.emit('tabClick', val.name)
      nextTick(initTabs)
    }

    // 关闭标签页
    const tabsCloseClick = (val: TabsListProps) => {
      tabsTitleList.value = tabsTitleList.value.filter((item: TabsListProps) => item.name !== val.name)
      ctx.emit('tabClose', val.name)
      nextTick(initTabs)
    }

    // 添加标签点击事件
    const addClick = () => {
      ctx.emit('tabAdd')
      nextTick(() => pathClick('right', true))
    }

    // 处理超出长度
    const pathClick = (path: 'left' | 'right', toMax = false) => {
      initTabs()
      if (!tabsTitleInnerDom?.value || !pathBtnShow.value) return
      const children = tabsTitleInnerDom.value.children[0] as HTMLElement
      const scrollWidth = children.scrollWidth
      const clientWidth = tabsTitleInnerDom.value.clientWidth
      const translate = children?.style.transform || '0'
      const x = translate?.replace(/^translateX\(/, '').replace(/px\)$/, '')
      const max = -(scrollWidth - clientWidth)
      const step = clientWidth / 2
      let num = path === 'right' ? Number(x) - step : Number(x) + step
      if (num >= 0) num = 0
      if (num <= max) num = max
      children.style[Transform] = `translateX(${toMax ? max : num}px)`
    }

    // 初始化标签页 判断是否超出长度
    const initTabs = () => {
      if (!tabsTitleInnerDom.value) return
      const children = tabsTitleInnerDom.value.children[0] as HTMLElement
      const scrollWidth = children.scrollWidth
      const clientWidth = tabsTitleInnerDom.value.clientWidth
      pathBtnShow.value = scrollWidth > clientWidth
    }

    // 初始化 line 选中
    const initLineType = () => {
      if (!tabsTitleInnerDom.value) return
      const child = tabsTitleInnerDom.value?.children[0].children
      const index = tabsTitleList.value.findIndex((item: TabsListProps) => item.name === currentActive.value)
      const childEl = child[index] as HTMLElement
      if (tabsActiveLineDom?.value && childEl) {
        tabsActiveLineDom.value.style.width = `${childEl.offsetWidth}px`
        tabsActiveLineDom.value.style.left = `${childEl.offsetLeft}px`
      }
    }

    onBeforeMount(() => {
      tabsMitt.on<TabsListProps | undefined>('tabsItemEmit', handleTabsItemEmit)
    })

    onMounted(() => {
      nextTick(() => {
        initTabs()
        initLineType()
      })
      window.addEventListener('resize', initTabs)
    })

    onBeforeUnmount(() => {
      window.addEventListener('resize', initTabs)
      tabsMitt.off('tabsItemEmit', handleTabsItemEmit)
    })

    return {
      currentActive,
      tabsTitleList,
      tabsActiveLineDom,
      tabsTitleInnerDom,
      pathBtnShow,
      titleStyle,
      tabsTitleClick,
      tabsCloseClick,
      addClick,
      pathClick
    }
  }
})
</script>
